package my.rpc.service;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import my.rpc.common.bean.RpcRequest;
import my.rpc.common.bean.RpcResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.Map;

/**
 * 服务器端Handler
 * @author fengbo
 * @date 2017/11/2
 */
class RpcServerHandler extends ChannelInboundHandlerAdapter {

    private static final Logger log = LoggerFactory.getLogger(RpcServerHandler.class);

    private Map<String, Object> handlerMap;

    RpcServerHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        log.info("client host = {}, post = {} registered", address.getHostName(), address.getPort());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        log.info("client host = {}, post = {} unregistered", address.getHostName(), address.getPort());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        RpcRequest request = (RpcRequest) msg;

        RpcResponse response = new RpcResponse();
        try {
            Object result = handle(request);
            response.setResult(result);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            response.setException(e);
        }
        ctx.writeAndFlush(response);
        // 不关闭连接，即使用TCP长连接
        //.addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 根据request中传递过来的接口的全额限定名从handlerMap中获取对应的对象
     * 并使用动态代理反射调用执行客户端需要执行的方法
     * @return 返回反射执行后该方法的执行的结果对象
     */
    private Object handle(RpcRequest request) throws InvocationTargetException {
        String serviceName = request.getInterfaceName();
        Object serviceBean = handlerMap.get(serviceName);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("can not find service bean by key: %s", serviceName));
        }

        // 获取反射调用所需的参数
        Class<?> serviceClass = serviceBean.getClass();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();
        Object result;
        try {
            Method method = serviceClass.getMethod(methodName, parameterTypes);
            result = method.invoke(serviceBean, parameters);
        } catch (NoSuchMethodException | IllegalAccessException e) {
            throw new IllegalStateException("invoke error!!!", e);
        }
        return result;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 客户端关闭的时候，这里会捕获一个异常
        log.error("service caught exception!", cause);
        ctx.close();
    }
}
